﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.DL.Data.Common;
using ScrumTable.DL.Data.Common.Config.Driver;
using ScrumTable.DL.Data.Generic.Properties;

#endregion

namespace ScrumTable.DL.Data.Generic.Config.Driver
{
    /// <summary>
    /// Represents the base selector for all driver configuration files which
    /// are serialized by the current driver.
    /// </summary>
    public abstract class GenericDriverConfigSelectorBase<T> : IDriverConfigSelector
        where T : IDriverConfig, new()
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private const string ConfigExtension = ".xml";
        private const string ConfigMarker = "DriverConfig";
        private const string ConfigPostFix = "." + ConfigMarker + ConfigExtension;
        private const string ConfigOverwritePostFix = "." + ConfigMarker + "_Override" + ConfigExtension;

        private readonly IList<GenericDriverConfigSource<T>> _dataContainer = new List<GenericDriverConfigSource<T>>();
        private readonly string _internalConfigData;
        private readonly IDataAssemblyInfo _driverInfo;
        private int _selectedIndex = -1;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets/sets the selected driver config instance.
        /// </summary>
        public IDriverConfigSource Selected
        {
            get
            {
                if (_selectedIndex == -1)
                {
                    return _dataContainer[_dataContainer.Count - 1];
                }
                return _dataContainer[_selectedIndex];
            }
            set
            {
                PreCondition.AssertNotNull(value, "value");

                int containerIndex = _dataContainer.IndexOf(value as GenericDriverConfigSource<T>);

                if (containerIndex == -1)
                {
                    throw new InvalidOperationException(Resources.ExcDatSchemaSrcInvalidSourceSpecified);
                }
                _selectedIndex = containerIndex;
            }
        }

        /// <summary>
        /// Gets/sets the default driver config instance.
        /// </summary>
        public IDriverConfigSource Default
        {
            get { return _dataContainer.FirstOrDefault(); }
        }

        #endregion

        #region Constructors / Destructor

        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericDriverConfigSelectorBase{T}"/> class.
        /// </summary>
        protected GenericDriverConfigSelectorBase(string internalConfigData, IDataAssemblyInfo driverInfo)
        {
            PreCondition.AssertNotNullOrEmpty(internalConfigData, "internalConfigData");
            PreCondition.AssertNotNull(driverInfo, "driverInfo");

            _internalConfigData = internalConfigData;
            _driverInfo = driverInfo;

            CreateSourceList();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<IDriverConfigSource> GetEnumerator()
        {
            return (from dataSource in _dataContainer select (IDriverConfigSource)dataSource).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Creates a new driver source and returns the created instance.
        /// The new source will contain the default configuration data provided
        /// by the underlying data assembly driver.
        /// </summary>
        public IDriverConfigSource Create()
        {
            // add internal data - this defines the driver default config
            T internalData = CreateConfig(_internalConfigData);
            GenericDriverConfigSource<T> driverConfigSource = new GenericDriverConfigSource<T>(
                internalData,
                _driverInfo.MapConfigFilePath(Guid.NewGuid().ToString("D") + ConfigPostFix));
            Add(driverConfigSource);
            return driverConfigSource;
        }

        /// <summary>
        /// Creates a new configuration instance out of the given
        /// xml configuration data.
        /// </summary>
        /// <param name="configData">Specifies a string which contains all configuration data to deserialize.</param>
        /// <returns>Returns the deserialized configuration instance.</returns>
        protected abstract T CreateConfig(string configData);

        /// <summary>
        /// Creates a new empty configuration instance.
        /// </summary>
        protected virtual T CreateEmptyConfig()
        {
            return new T();
        }


        private void CreateSourceList()
        {
            // add internal data - this defines the driver default config
            T internalData = CreateConfig(_internalConfigData);
            Add(new GenericDriverConfigSource<T>(internalData, string.Empty));

            FillCustomDriverConfig();
            FillConfigFromUserDirectory();
            FillOverwriteDriverConfig();
        }

        private void FillOverwriteDriverConfig()
        {
            string configFilePath = _driverInfo.MapConfigFilePath(GetType().Assembly.GetName().Name + ConfigOverwritePostFix);

            if (File.Exists(configFilePath))
            {
                Add(new GenericDriverConfigSource<T>(
                    CreateConfig(File.ReadAllText(configFilePath)),
                    configFilePath));
            }
        }

        private void FillCustomDriverConfig()
        {
            string configFilePath = _driverInfo.MapConfigFilePath(GetType().Assembly.GetName().Name + ConfigPostFix);

            if (File.Exists(configFilePath))
            {
                Add(new GenericDriverConfigSource<T>(
                    CreateConfig(File.ReadAllText(configFilePath)),
                    configFilePath));
            }
        }

        private void FillConfigFromUserDirectory()
        {
            foreach (var configFile in Directory.GetFiles(_driverInfo.ConfigFolderPath, "*" + ConfigPostFix))
            {
                Add(new GenericDriverConfigSource<T>(
                    CreateConfig(File.ReadAllText(configFile)),
                    configFile));
            }
        }

        private void Add(GenericDriverConfigSource<T> driverConfig)
        {
            _dataContainer.Add(driverConfig);
        }

        #endregion

        #region Events

        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
